package org.luxor.commons.jdbc.aspect;

import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.luxor.commons.core.utils.StringUtils;
import org.luxor.commons.core.utils.encrypt.AesEncryptUtils;
import org.luxor.commons.jdbc.annotation.EncryptHandle;
import org.luxor.commons.jdbc.plugin.dynamic.properties.DynamicDataSourceProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 方法参数脱敏切面类
 *
 * @author Mr.Yan  @date 2019/9/30
 */
@Aspect
public class EncryptHandleAspect {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final DynamicDataSourceProperties properties;

    public EncryptHandleAspect(DynamicDataSourceProperties properties) {
        this.properties = properties;
    }

    @Around("@annotation(org.luxor.commons.jdbc.annotation.EncryptHandle)")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        EncryptHandle encryptHandle = method.getAnnotation(EncryptHandle.class);
        //获取注解中的加密字段值，转换为数组
        String[] columns = encryptHandle.column().split(",");
        String[] columnTrimSpaces = encryptHandle.columnTrimSpace().split(",");
        //flag用于判断是否全部字段都加密
        boolean flag = false;
        if ("".equals(columns[0])) {
            columns = null;
        }
        if ("".equals(columnTrimSpaces[0])) {
            columnTrimSpaces = null;
        }
        if (columns == null && columnTrimSpaces == null) {
            flag = true;
        }
        Object[] args = point.getArgs();
        String[] argNames = ((CodeSignature) point.getSignature()).getParameterNames();
        if (args.length > 0) {
            //传入的参数类型可能为多个String或HashMap，分别进行遍历处理
            for (int i = 0; i < args.length; i++) {
                Object param = args[i];
                if (param == null) {
                    continue;
                }
                if (!param.getClass().getName().startsWith("java.")) {
                    param = objectToMap(args[i]);
                }
                //如果传入参数类型为HashMap，遍历处理
                if (param instanceof HashMap) {
                    HashMap<String, Object> hashMap = (HashMap<String, Object>) param;
                    for (Map.Entry<String, Object> entry : hashMap.entrySet()) {
                        //仅当类型为String时才进行下一步
                        if (entry.getValue() instanceof String) {
                            if (flag || (columns != null && ArrayUtils.contains(columns, entry.getKey()))) {
                                logger.info("EncryptHandleAspect" + method.getName() + " column " + entry.getKey());
                                if (doDecrypt((String) entry.getValue(), 0) == null) {
                                    hashMap.put(entry.getKey(), doEncrypt((String) entry.getValue(), 0));
                                }
                            } else if (columnTrimSpaces != null && ArrayUtils.contains(columnTrimSpaces, entry.getKey())) {
                                logger.info("EncryptHandleAspect " + method.getName() + " columnTrimSpace " + entry.getKey());
                                if (doDecrypt((String) entry.getValue(), 1) == null) {
                                    hashMap.put(entry.getKey(), doEncrypt((String) entry.getValue(), 1));
                                }
                            }
                        }
                    }
                } else if (param instanceof String) {
                    //仅当类型为String时才进行下一步
                    if (flag || (columns != null && ArrayUtils.contains(columns, argNames[i]))) {
                        logger.info("EncryptHandleAspect " + method.getName() + " column " + argNames[i]);
                        if (doDecrypt((String) param, 0) == null) {
                            args[i] = doEncrypt((String) param, 0);
                        }
                    } else if (columnTrimSpaces != null && ArrayUtils.contains(columnTrimSpaces, argNames[i])) {
                        logger.info("EncryptHandleAspect " + method.getName() + " columnTrimSpace " + argNames[i]);
                        if (doDecrypt((String) param, 1) == null) {
                            args[i] = doEncrypt((String) param, 1);
                        }
                    }
                }
            }
        }

        //执行方法
        Object result = point.proceed();

        return result;
    }

    public String doEncrypt(String val, int type) {
        if (StringUtils.isBlank(val)) {
            return val;
        }
        if (type == 1) {
            val = StringUtils.trimSpace(val);
        }
        if (StringUtils.isEmpty(properties.getEncryptSecret())) {
            return AesEncryptUtils.encrypt(val);
        }
        return AesEncryptUtils.encrypt(val, properties.getEncryptSecret());
    }

    public String doDecrypt(String val, int type) {
        if (!org.springframework.util.StringUtils.hasText(val)) {
            return val;
        }
        if (type == 1) {
            val = StringUtils.trimSpace(val);
        }
        if (StringUtils.isEmpty(properties.getEncryptSecret())) {
            return AesEncryptUtils.decrypt(val);
        }
        return AesEncryptUtils.decrypt(val, properties.getEncryptSecret());
    }

    public static Map<String, Object> objectToMap(Object obj) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>(8);
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            String fieldName = field.getName();
            map.put(fieldName, field.get(obj));
        }
        return map;
    }
}
